package notorious.rascal.variometer;

import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.Message;
import android.os.SystemClock;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.app.Activity;
import android.app.PendingIntent;
import android.view.Menu;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
import android.widget.TextView;

import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import java.lang.Thread;

import notorious.rascal.variometer.model.*;
import notorious.rascal.variometer.audio.BeepThread;


public class VarioActivity extends Activity implements Runnable, VarioChangeListener {
	static final String TAG = "Variometer";
	
	private static final String ACTION_USB_PERMISSION = "notorious.rascal.variometer.action.USB_PERMISSION";
	
	private UsbManager mUsbManager;
	private PendingIntent mPermissionIntent;
	private boolean mPermissionRequestPending;
	
	PowerManager mPowerManager;
	WakeLock mWakeLock;
	
	UsbAccessory mAccessory;
	ParcelFileDescriptor mFileDescriptor;
	FileInputStream mInputStream;
	FileOutputStream mOutputStream;
	
	KalmanFilteredVario mKalmanVario;
	KalmanFilteredVario mDampedVario;
	KalmanFilteredAltitude mAltitude;
	BeepThread mBeeps;
	
	private long mLastTick;
	
	TextView mMessage;
	
	private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if (ACTION_USB_PERMISSION.equals(action)) {
				synchronized (this) {
					UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); // UsbManager.getAccessory(intent);
					if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
						openAccessory(accessory);
					} else {
						Log.d(TAG, "permission denied for accessory " + accessory);
					}
					mPermissionRequestPending = false;
				}
			} else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
				UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); // UsbManager.getAccessory(intent);
				if (accessory != null && accessory.equals(mAccessory)) {
					closeAccessory();
				}
			}
		}
	};
	
	private final Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			final long currTick = SystemClock.elapsedRealtime();
			final long dt = currTick - mLastTick;
			
			Log.i(TAG, "pressure = " + msg.arg1 + " tick = " + msg.arg2);
			mAltitude.addPressure(msg.arg1 / 100., msg.arg2 / 100.);
		}
	};

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_vario);

		//
		mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);

        //
        mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE); // UsbManager.getInstance(this);
        mPermissionIntent = PendingIntent.getBroadcast(this,  0,  new Intent(ACTION_USB_PERMISSION), 0);
        
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
        registerReceiver(mUsbReceiver, filter);
        
        if( getLastNonConfigurationInstance() != null ) {
        	mAccessory = (UsbAccessory)getLastNonConfigurationInstance();
        	openAccessory(mAccessory);        	
        }
        
        //
        mMessage = (TextView)findViewById(R.id.message);

        // setup Altitude
        mAltitude = new KalmanFilteredAltitude(1013.25, "Altimeter");
        mAltitude.setAltDamp(0.05);
        mAltitude.setPositionNoise(0.2);
        
        mKalmanVario = mAltitude.getKalmanVario();
        
        mDampedVario = mAltitude.getDampedVario();
        mDampedVario.setVarDamp(0.05);
        
        mKalmanVario.addChangeListener(this);
    }
    
    @Override
    public Object onRetainNonConfigurationInstance() {
    	if (mAccessory != null) {
    		return mAccessory;
    	} else {
    		return super.onRetainNonConfigurationInstance();
    	}
    }

	@Override
	public void onResume() {
		super.onResume();

		Intent intent = getIntent();
		if (mInputStream != null && mOutputStream != null) {
			return;
		}

		UsbAccessory[] accessories = mUsbManager.getAccessoryList();
		UsbAccessory accessory = (accessories == null ? null : accessories[0]);
		if (accessory != null) {
			if (mUsbManager.hasPermission(accessory)) {
				openAccessory(accessory);
			} else {
				synchronized (mUsbReceiver) {
					if (!mPermissionRequestPending) {
						mUsbManager.requestPermission(accessory,
								mPermissionIntent);
						mPermissionRequestPending = true;
					}
				}
			}
		} else {
			Log.d(TAG, "mAccessory is null");
		}
		
		//
		//if( mWakeLock != null )
		//	mWakeLock.release();
		//
		//mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "Vario");
		//mWakeLock.acquire();
	}

	@Override
	public void onPause() {
		super.onPause();
		
		if( mBeeps != null ) {
			mBeeps.onDestroy();
			mBeeps = null;
		}
		closeAccessory();
		
		//if( mWakeLock != null ) {
		//	mWakeLock.release();
		//	mWakeLock = null;
		//}
	}

	@Override
	public void onDestroy() {
		unregisterReceiver(mUsbReceiver);
		super.onDestroy();
	}

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_vario, menu);
        return true;
    }
    
    static final String HEXES = "0123456789ABCDEF";
    public static String getHex( byte [] raw ) {
        if ( raw == null ) {
            return null;
        }
        final StringBuilder hex = new StringBuilder( 3 * raw.length );
        for( int i = 0; i < 8; i++ ) {
        	byte b = raw[i];
            hex.append(HEXES.charAt((b & 0xF0) >> 4))
                .append(HEXES.charAt((b & 0x0F))).append(' ');
        }
        return hex.toString();
    }
    
    long toLong(byte b) {
    	return 0;
    }
    
    long toLong(byte b0, byte b1, byte b2, byte b3) {
    	return 0;
    }

    public void run() {
    	int ret = 0;
    	byte[] buf = new byte[16384];
    	
    	while (ret >= 0) {
    		//
    		try {
    			ret = mInputStream.read(buf);    			
    		} catch (IOException e) {
    			break;
    		}
    		
    		//
    		if( ret == 8 )
    		{
    			int tick = 0;
    			int pressure = 0;
        		
    			tick |= (((int)buf[0])<<24) & 0x00FF000000;
    			tick |= (((int)buf[1])<<16) & 0x0000FF0000;
    			tick |= (((int)buf[2])<< 8) & 0x000000FF00;
    			tick |= (((int)buf[3])<< 0) & 0x00000000FF;

    			pressure |= (((int)buf[4])<<24) & 0xFF000000;
    			pressure |= (((int)buf[5])<<16) & 0x00FF0000;
    			pressure |= (((int)buf[6])<< 8) & 0x0000FF00;
    			pressure |= (((int)buf[7])<< 0) & 0x000000FF;
    			
    			mHandler.obtainMessage(0, pressure, tick).sendToTarget();
    		}
    	}
    }

    private void openAccessory(UsbAccessory accessory) {
		mFileDescriptor = mUsbManager.openAccessory(accessory);
		if (mFileDescriptor != null) {
			mAccessory = accessory;
			FileDescriptor fd = mFileDescriptor.getFileDescriptor();
			mInputStream = new FileInputStream(fd);
			mOutputStream = new FileOutputStream(fd);
			mLastTick = SystemClock.elapsedRealtime();
			
			//
			Thread thread = new Thread(null, this, "PressureAccessory");
			thread.start();
			Log.d(TAG, "accessory opened");
			
			//
			setBeeps(true);
			
			//
			if( mWakeLock != null )
				mWakeLock.release();
			
			mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "Vario");
			mWakeLock.acquire();
		} else {
			Log.d(TAG, "accessory open fail");
		}
	}

	private void closeAccessory() {
		try {
			//
			if( mWakeLock != null ) {
				mWakeLock.release();
				mWakeLock = null;
			}

			//
			setBeeps(false);
			
			//
			if (mFileDescriptor != null) {
				mFileDescriptor.close();
			}
		} catch (IOException e) {
		} finally {
			mFileDescriptor = null;
			mAccessory = null;
		}
	}
	
	private void setBeeps(boolean enable) {
		if( enable ) {
			if( mBeeps != null ) 
				mBeeps.setRunning(false);
			
			mBeeps = new BeepThread(this, mKalmanVario);
		} else {
			if( mBeeps != null ) { 
				mBeeps.setRunning(false);
				mBeeps = null;
			}
		}
		
	}

    public synchronized void varioChanged(double newVar) {
    	mMessage.setText("Vario = " + newVar);
    }
}
